package org.sso.common.cookie;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sso.common.cons.Const;
import org.sso.common.cons.ParamConst;
import org.sso.common.domian.SsoException;
import org.sso.common.domian.Ticket;
import org.sso.common.domian.Token;
import org.sso.common.domian.TokenBase;
import org.sso.common.encrypt.AES;
import org.sso.common.encrypt.SSOEncrypt;
import org.sso.common.util.RandomUtil;

public class CookieHelper {

	private static final Logger logger = LoggerFactory.getLogger(CookieHelper.class);

	/* 立即删除 */
	public final static int CLEAR_IMMEDIATELY_REMOVE = 0;

	/**
	 * <p>
	 * 根据cookieName获取Cookie
	 * </p>
	 * 
	 * @param request
	 * @param cookieName
	 *            Cookie name
	 * @return Cookie
	 */
	public static Cookie findCookieByName(HttpServletRequest request, String cookieName) {
		Cookie[] cookies = request.getCookies();
		if (cookies == null)
			return null;
		for (int i = 0; i < cookies.length; i++) {
			if (cookies[i].getName().equals(cookieName)) {
				return cookies[i];
			}
		}
		return null;
	}

	/**
	 * 
	 * <p>
	 * 根据cookieName清除指定Cookie
	 * </p>
	 * 
	 * @param request
	 * @param response
	 * @param cookieName
	 *            cookie name
	 * @param domain
	 *            Cookie所在的域
	 * @param path
	 *            Cookie 路径
	 * @return boolean
	 */
	public static boolean clearCookieByName(HttpServletRequest request, HttpServletResponse response, String cookieName,
			String domain, String path) {
		boolean result = false;
		Cookie ck = findCookieByName(request, cookieName);
		if (ck != null) {
			result = clearCookie(response, cookieName, domain, path);
		}
		return result;
	}

	/**
	 * <p>
	 * 清除指定Cookie 等同于 clearCookieByName(...)
	 * </p>
	 * 
	 * <p>
	 * 该方法不判断Cookie是否存在,因此不对外暴露防止Cookie不存在异常.
	 * </p>
	 * 
	 * @param response
	 * @param cookieName
	 *            cookie name
	 * @param domain
	 *            Cookie所在的域
	 * @param path
	 *            Cookie 路径
	 * @return boolean
	 */
	private static boolean clearCookie(HttpServletResponse response, String cookieName, String domain, String path) {
		boolean result = false;
		try {
			Cookie cookie = new Cookie(cookieName, "");
			cookie.setMaxAge(CLEAR_IMMEDIATELY_REMOVE);
			cookie.setDomain(domain);
			cookie.setPath(path);
			response.addCookie(cookie);
			logger.info("clear cookie " + cookieName);
			result = true;
		} catch (Exception e) {
			logger.error("clear cookie " + cookieName + " is exception!\n" + e.toString());
		}
		return result;
	}

	/**
	 * <p>
	 * 当前访问域下设置登录Cookie
	 * </p>
	 * 
	 * @param request
	 * @param response
	 * @param encrypt
	 *            对称加密算法类
	 */
	public static void setSSOCookie(HttpServletRequest request, HttpServletResponse response, String domain,
			Token token, SSOEncrypt encrypt) {
		if (encrypt == null) {
			throw new SsoException(" Encrypt not for null.");
		}
		try {
			/**
			 * 设置加密 Cookie
			 */
			Cookie ck = generateSsoCookie(request, domain, token, encrypt);
			addHttpOnlyCookie(response, ck);
			/**
			 * Cookie设置HttpOnly
			 */
			// if (config.getCookieHttponly()) {
			// CookieHelper.addHttpOnlyCookie(response, ck);
			// } else {
			response.addCookie(ck);
			// }
		} catch (Exception e) {
			logger.error("set HTTPOnly cookie createAUID is exception! " + e.toString());
		}
	}

	/**
	 * <p>
	 * 根据Token生成登录信息Cookie
	 * </p>
	 * 
	 * @param request
	 * @param token
	 *            SSO 登录信息票据
	 * @param encrypt
	 *            对称加密算法类
	 * @return Cookie 登录信息Cookie {@link Cookie}
	 */
	public static Cookie generateSsoCookie(HttpServletRequest request, String domain, Token token, SSOEncrypt encrypt) {
		try {
			Cookie cookie = new Cookie(Const.cookieName, encryptCookie(request, token, encrypt));
			cookie.setPath(Const.cookie_path);
			// 只允许http传输
			cookie.setSecure(false);
			/**
			 * domain 提示
			 * <p>
			 * 有些浏览器 localhost 无法设置 cookie
			 * </p>
			 */
			// String domain = ParamConst.cookie_domain;
			cookie.setDomain(domain);
			if ("".equals(domain) || domain.contains("localhost")) {
				logger.info("if you can't login, please enter normal domain. instead:" + domain);
			}

			/**
			 * 设置Cookie超时时间
			 */
			int maxAge = Const.COOKIE_MAX_AGE;
			Integer attrMaxAge = (Integer) request.getAttribute(Const.SSO_COOKIE_MAXAGE);
			if (attrMaxAge != null) {
				maxAge = attrMaxAge;
			}
			if (maxAge >= 0) {
				cookie.setMaxAge(maxAge);
			}
			return cookie;
		} catch (Exception e) {
			logger.info("generateCookie is exception!" + e.toString());
			return null;
		}
	}

	/**
	 * <p>
	 * 加密Token信息
	 * </p>
	 * 
	 * @param request
	 * @param token
	 *            SSO 登录信息票据
	 * @param encrypt
	 *            对称加密算法类
	 * @return Cookie 登录信息Cookie {@link Cookie}
	 */
	private static String encryptCookie(HttpServletRequest request, TokenBase token, SSOEncrypt encrypt)
			throws Exception {
		if (token == null) {
			throw new SsoException(" Token not for null.");
		}
		/**
		 * token加密混淆
		 */
		String jt = token.jsonToken();
		StringBuffer sf = new StringBuffer();
		sf.append(jt);
		sf.append(Const.CUT_SYMBOL);
		sf.append(RandomUtil.getCharacterAndNumber(8));
		return encrypt.encrypt(sf.toString(), ParamConst.AES_KEY);
	}

	/**
	 * 
	 * <p>
	 * 添加 Cookie
	 * </p>
	 * 
	 * @param response
	 * @param domain
	 *            所在域
	 * @param path
	 *            域名路径
	 * @param name
	 *            名称
	 * @param value
	 *            内容
	 * @param maxAge
	 *            生命周期参数
	 * @param httpOnly
	 *            只读
	 * @param secured
	 *            Https协议下安全传输
	 */
	public static void addCookie(HttpServletRequest request, HttpServletResponse response, String domain, String path,
			String name, Ticket tic, int maxAge, boolean httpOnly, boolean secured) {
		try {
			String value = encryptCookie(request, tic, AES.getInstance());
			Cookie cookie = new Cookie(name, value);
			/**
			 * 不设置该参数默认 当前所在域
			 */
			if (domain != null && !"".equals(domain)) {
				cookie.setDomain(domain);
			}
			cookie.setPath(path);
			cookie.setMaxAge(maxAge);

			/** Cookie 只在Https协议下传输设置 */
			if (secured) {
				cookie.setSecure(secured);
			}

			/** Cookie 只读设置 */
			if (httpOnly) {
				addHttpOnlyCookie(response, cookie);
			} else {
				/*
				 * //servlet 3.0 support cookie.setHttpOnly(httpOnly);
				 */
				response.addCookie(cookie);
			}
		} catch (Exception e) {
			logger.error("AuthToken encryptCookie error.\n" + e.toString());
		}
	}

	/**
	 * 
	 * <p>
	 * 解决 servlet 3.0 以下版本不支持 HttpOnly
	 * </p>
	 * 
	 * @param response
	 *            HttpServletResponse类型的响应
	 * @param cookie
	 *            要设置httpOnly的cookie对象
	 */
	public static void addHttpOnlyCookie(HttpServletResponse response, Cookie cookie) {
		if (cookie == null) {
			return;
		}
		/**
		 * 依次取得cookie中的名称、值、 最大生存时间、路径、域和是否为安全协议信息
		 */
		String cookieName = cookie.getName();
		String cookieValue = cookie.getValue();
		int maxAge = cookie.getMaxAge();
		String path = cookie.getPath();
		String domain = cookie.getDomain();
		boolean isSecure = cookie.getSecure();
		StringBuffer sf = new StringBuffer();
		sf.append(cookieName + "=" + cookieValue + ";");

		if (maxAge >= 0) {
			sf.append("Max-Age=" + cookie.getMaxAge() + ";");
		}

		if (domain != null) {
			sf.append("domain=" + domain + ";");
		}

		if (path != null) {
			sf.append("path=" + path + ";");
		}

		if (isSecure) {
			sf.append("secure;HTTPOnly;");
		} else {
			sf.append("HTTPOnly;");
		}

		response.addHeader("Set-Cookie", sf.toString());
	}
}
